﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Server;
using ESRI.ArcGIS.SOESupport;


namespace WorldViewSolutions.ArcGISServer.SOE
{
    /// <summary>
    /// 
    /// </summary>
    /// <remarks></remarks>
	[ComVisible(true)]
    [Guid("CFF5B190-082D-47cc-A148-BAB4CBC0A68A")]
	[ClassInterface(ClassInterfaceType.None)]
	public class PrintSOE : ServicedComponent, IServerObjectExtension, IObjectConstruct, IRESTRequestHandler, ILogSupport
	{
        /// <summary>
        /// 
        /// </summary>
		private const string SOE_NAME = "PrintSOE";

        /// <summary>
        /// 
        /// </summary>
		private IRESTRequestHandler _restRequestHandler;

        /// <summary>
        /// 
        /// </summary>
		private IServerObjectHelper _serverObjectHelper;

        /// <summary>
        /// 
        /// </summary>
		private ESRI.ArcGIS.esriSystem.ILog _log;

        /// <summary>
        /// 
        /// </summary>
		private string _templatesDirectory;

        /// <summary>
        /// 
        /// </summary>
		private List<PrintTemplate> _templates;

        /// <summary>
        /// 
        /// </summary>
        private string _outputDir = null;

        /// <summary>
        /// 
        /// </summary>
        private string _virtualOutputDir = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.EnterpriseServices.ServicedComponent"/> class.
        /// </summary>
        /// <remarks></remarks>
		public PrintSOE()
        {
            RestResource rootResource = CreateRestSchema();

			SoeRestImpl restImpl = new SoeRestImpl(SOE_NAME, rootResource);
			_restRequestHandler = (IRESTRequestHandler)restImpl;

        }

        /// <summary>
        /// Creates the rest schema.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
		private RestResource CreateRestSchema()
		{
			RestResource rrSOE = new RestResource(SOE_NAME, false, RootSOE);
            RestResource rrTemplates = new RestResource("Templates", true, GetTemplateHandler);

			RestOperation roGetThumbnail = new RestOperation("Thumbnail",
													   null,
													   new string[] { "gif" },
                                                       GetThumbnailHandler);
       

            
            RestOperation roExportTemplate = new RestOperation("Export",
													   new string[] {
                                                            "bbox", 
                                                            "bboxSR", 
                                                            "scale", 
                                                            "backgroundColor",
                                                            "resources", 
                                                            "graphics",
                                                            "textElements",
                                                            "attributes"
                                                       },
													   new string[] { "json" },
                                                       ExportTemplateHandler,
                                                       true);

            RestOperation roExportImage = new RestOperation("ExportImage",
                                                       new string[] {
                                                            "bbox", 
                                                            "bboxSR", 
                                                            "scale", 
                                                            "size", 
                                                            "backgroundColor",
                                                            "format",
                                                            "resources", 
                                                            "graphics"
                                                       },
                                                       new string[] { "json", "image" },
                                                       ExportImageHandler,
                                                       true);
			rrSOE.resources.Add(rrTemplates);
            rrSOE.operations.Add(roExportImage);
            rrTemplates.operations.Add(roExportTemplate);
            rrTemplates.operations.Add(roGetThumbnail);
			return rrSOE;
		}

        /// <summary>
        /// Exports the image handler.
        /// </summary>
        /// <param name="boundVariables">The bound variables.</param>
        /// <param name="operationInput">The operation input.</param>
        /// <param name="outputFormat">The output format.</param>
        /// <param name="requestProperties">The request properties.</param>
        /// <param name="responseProperties">The response properties.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private byte[] ExportImageHandler(NameValueCollection boundVariables, JsonObject operationInput, string outputFormat, string requestProperties, out string responseProperties)
        {
            responseProperties = null;

            try
            {
                ExportParameters exportParameters = new ExportParameters( operationInput);
                exportParameters.FixExent();
                
                System.Drawing.Bitmap mapImage = MapExport.CreateBitmap(exportParameters);

                string fileName = MapExport.SaveToFile(mapImage, exportParameters.Format, _outputDir);              
                var url = UrlHelper.Combine(_virtualOutputDir, fileName);

                JsonObject jObject = new JsonObject();
                jObject.AddString("url", url);

                return Encoding.UTF8.GetBytes(jObject.ToJson());
            }
            catch (Exception ex)
            {
                ArcGISLog.WriteError("ERROR: " + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
        }

        /// <summary>
        /// Exports the template handler.
        /// </summary>
        /// <param name="boundVariables">The bound variables.</param>
        /// <param name="operationInput">The operation input.</param>
        /// <param name="outputFormat">The output format.</param>
        /// <param name="requestProperties">The request properties.</param>
        /// <param name="responseProperties">The response properties.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private byte[] ExportTemplateHandler(NameValueCollection boundVariables, JsonObject operationInput, string outputFormat, string requestProperties, out string responseProperties)
        {
            responseProperties = null;

            try
            {
                ExportParameters exportParameters = new ExportParameters(operationInput);

                // TemplatesID is defined by internal ESRI methods
                int id = Convert.ToInt32(boundVariables["TemplatesID"]);
                if (!_templates.Exists(t => t.ID == id))
                    throw new Exception("Invalid template");

                PrintTemplate template = _templates.Find(t => t.ID == id);

                template.OutputDir = _outputDir;
                string fileName = template.CreatePDF(exportParameters);

                var url = UrlHelper.Combine(_virtualOutputDir, fileName);

                JsonObject jObject = new JsonObject();
                jObject.AddString("url", url);

                return Encoding.UTF8.GetBytes(jObject.ToJson());
            }
            catch (Exception ex)
            {
                ArcGISLog.WriteError("ERROR: " + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
        }

        // Templates/{template id}
        /// <summary>
        /// Gets the template handler.
        /// </summary>
        /// <param name="boundVariables">The bound variables.</param>
        /// <param name="outputFormat">The output format.</param>
        /// <param name="requestProperties">The request properties.</param>
        /// <param name="responseProperties">The response properties.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private byte[] GetTemplateHandler(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties)
        {
            responseProperties = null;

            // TemplatesID is defined by internal ESRI methods
            int id = Convert.ToInt32(boundVariables["TemplatesID"]);
            PrintTemplate template = _templates.Find(t => t.ID == id);
            string json = template.ToJsonObject().ToJson();
            return Encoding.UTF8.GetBytes(json);
        }

        /// <summary>
        /// Gets the thumbnail handler.
        /// </summary>
        /// <param name="boundVariables">The bound variables.</param>
        /// <param name="operationInput">The operation input.</param>
        /// <param name="outputFormat">The output format.</param>
        /// <param name="requestProperties">The request properties.</param>
        /// <param name="responseProperties">The response properties.</param>
        /// <returns></returns>
        /// <remarks></remarks>
		private byte[] GetThumbnailHandler(System.Collections.Specialized.NameValueCollection boundVariables, ESRI.ArcGIS.SOESupport.JsonObject operationInput, string outputFormat, string requestProperties, out string responseProperties)
		{
            responseProperties = "{\"Content-Type\" : \"image/gif\"}";


			int id = Convert.ToInt32(boundVariables["TemplatesID"]);
			PrintTemplate template = _templates.Find(t => t.ID == id);

			if (template.HasThumbnail)
			{
				System.IO.MemoryStream ms = new System.IO.MemoryStream();
				template.Thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
				return ms.GetBuffer();
			}
			else
			{
				throw new Exception("Thumbnail not available");
			}
		}

        /// <summary>
        /// Roots the SOE.
        /// </summary>
        /// <param name="boundVariables">The bound variables.</param>
        /// <param name="outputFormat">The output format.</param>
        /// <param name="requestProperties">The request properties.</param>
        /// <param name="responseProperties">The response properties.</param>
        /// <returns></returns>
        /// <remarks></remarks>
		private byte[] RootSOE(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties)
		{
			responseProperties = null;
			

			List<JsonObject> joTemplates = new List<JsonObject>();
			foreach (PrintTemplate t in _templates)
			{
				joTemplates.Add(t.ToJsonObject());
			}			

			JsonObject result = new JsonObject();
			result.AddArray("templates", joTemplates.ToArray());
			string json = result.ToJson();
			return Encoding.UTF8.GetBytes(json);
		}



        /// <summary>
        /// Gets the encoder.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private static ImageCodecInfo GetEncoder(System.Drawing.Imaging.ImageFormat format)
        {

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }

		#region IRESTRequestHandler Members

        /// <summary>
        /// Gets the schema.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
		public string GetSchema()
		{
			return _restRequestHandler.GetSchema();
		}

        /// <summary>
        /// Handles the REST request.
        /// </summary>
        /// <param name="Capabilities">The capabilities.</param>
        /// <param name="resourceName">Name of the resource.</param>
        /// <param name="operationName">Name of the operation.</param>
        /// <param name="operationInput">The operation input.</param>
        /// <param name="outputFormat">The output format.</param>
        /// <param name="requestProperties">The request properties.</param>
        /// <param name="responseProperties">The response properties.</param>
        /// <returns></returns>
        /// <remarks></remarks>
		public byte[] HandleRESTRequest(string Capabilities, string resourceName, string operationName, string operationInput, string outputFormat, string requestProperties, out string responseProperties)
		{			
			return _restRequestHandler.HandleRESTRequest(Capabilities, resourceName, operationName, operationInput, outputFormat, requestProperties, out responseProperties);
		}

		#endregion

		#region IObjectConstruct Members

        /// <summary>
        /// Two phase object construction.
        /// </summary>
        /// <param name="props">The props.</param>
        /// <remarks></remarks>
		public void Construct(IPropertySet props)
		{
            _templates = new List<PrintTemplate>();

            _templatesDirectory = props.GetProperty("TemplateDirectory") as string;

            ArcGISLog.Write("TemplateDirectory: " + _templatesDirectory);

            if (!Directory.Exists(_templatesDirectory))
            {
                ArcGISLog.WriteError("Directory not found: " + _templatesDirectory);
                return;
            }

            DirectoryInfo dir = new DirectoryInfo(_templatesDirectory);
            int fileID = 0;
            FileInfo[] files = dir.GetFiles("*.mxd");
            var orderedFiles = files.OrderBy(f => f.Name);
            foreach (FileInfo file in orderedFiles)
            {
                ArcGISLog.Write("Adding template: " + file.FullName);

                PrintTemplate template = new PrintTemplate(fileID, file.FullName, _log, _outputDir);
           
                _templates.Add(template);
                fileID++;
            }
		}

		#endregion

		#region IServerObjectExtension Members

        /// <summary>
        /// Inits the specified helper.
        /// </summary>
        /// <param name="helper">The helper.</param>
        /// <remarks></remarks>
		public void Init(IServerObjectHelper helper)
		{
			_serverObjectHelper = helper;
           
           
            // Get the output directory and virtual URL to the output directory
            IPropertySet props = ((IServerObjectHelper2)helper).ServerObjectProperty;

            _outputDir = (string)props.GetProperty("OutputDir");
            _virtualOutputDir = (string)props.GetProperty("VirtualOutputDir");

            ArcGISLog.Write("OutputDir: " + _outputDir);
            ArcGISLog.Write("VirtualOutputDir: " + _virtualOutputDir);
            string configName = _serverObjectHelper.ServerObject.ConfigurationName + "_MapServer";
            _outputDir = System.IO.Path.Combine(_outputDir, configName);
            ArcGISLog.Write("_outputDir: " + _outputDir);
            _virtualOutputDir = UrlHelper.Combine(_virtualOutputDir, configName);
            ArcGISLog.Write("_virtualOutputDir: " + _virtualOutputDir);

		}


        /// <summary>
        /// Stops the server object specified by the IServerObjectHelper reference.
        /// </summary>
        /// <remarks></remarks>
		public void Shutdown()
		{
			_serverObjectHelper = null;
            ArcGISLog.Log = null;
			_log = null;
		}

		#endregion	
	
		#region ILogSupport Members

        /// <summary>
        /// Inits the logging.
        /// </summary>
        /// <param name="log">The log.</param>
        /// <remarks></remarks>
		public void InitLogging(ESRI.ArcGIS.esriSystem.ILog log)
		{
			_log = log;
            ArcGISLog.Log = _log;
            ArcGISLog.Write("Setting global logger");
		}

		#endregion

        
	}
}